package gbench.common.matlib.matrix;

import gbench.common.matlib.MatlibCanvas.IVPair;
import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.matlib.MatlibCanvas.XVec;
import gbench.common.matlib.rdd.BasePair;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collector;

import static java.lang.Math.PI;

/**
 * 复数类 x+yi <br>
 * 一个复数 也可被视为 一个平面上的点,所以复数 具有 丰富的几何性质 <br>
 *
 * @author gbench
 */
public class Complex extends BasePair<Double, Double> {

    /**
     * 构造函数
     *
     * @param a 实数部分
     * @param b 虚数部分
     */
    public Complex(final Number a, final Number b) {
        super(a.doubleValue(), b.doubleValue());
        final var real = a.doubleValue();
        final var imaginary = b.doubleValue();
        this.isNaN = Double.isNaN(real) || Double.isNaN(imaginary);
        this.isInfinite = !isNaN && (Double.isInfinite(real) || Double.isInfinite(imaginary));
    }

    /**
     * 构造函数
     *
     * @param ivp 数值对儿
     */
    public Complex(final IVPair<? extends Number, ? extends Number, ?> ivp) {
        this(ivp._1(), ivp._2());
    }

    /**
     * 实数部分
     *
     * @return Double
     */
    public Double x() {
        return this._1();
    }

    /**
     * 虚数部分
     *
     * @return Double
     */
    public Double y() {
        return this._2();
    }

    /**
     * 实数部分
     *
     * @return Double
     */
    public Double real() {
        return this._1();
    }

    /**
     * 虚数部分
     *
     * @return Double
     */
    public Double imaginary() {
        return this._2();
    }

    /**
     * Compute the argument of this complex number.
     * The argument is the angle phi between the positive real axis and
     * the point representing this number in the complex plane.
     * The value returned is between -PI (not inclusive)
     * and PI (inclusive), with negative values returned for numbers with
     * negative imaginary parts.
     * <p>
     * If either real or imaginary part (or both) is NaN, NaN is returned.
     * Infinite parts are handled as {@code Math.atan2} handles them,
     * essentially treating finite parts as zero in the presence of an
     * infinite coordinate and returning a multiple of pi/4 depending on
     * the signs of the infinite parts.
     * See the javadoc for {@code Math.atan2} for full details.
     *
     * @return the argument of {@code this}.
     */
    public double getArgument() {
        return Math.atan2(this.imaginary(), this.real());
    }

    /**
     * Return the absolute value of this complex number.
     * Returns {@code NaN} if either real or imaginary part is {@code NaN}
     * and {@code Double.POSITIVE_INFINITY} if neither part is {@code NaN},
     * but at least one part is infinite.
     *
     * @return the absolute value.
     */
    public double abs() {
        final var real = this.real();
        final var imaginary = this.imaginary();
        if (Math.abs(real) < Math.abs(imaginary)) {
            if (imaginary == 0.0) {
                return Math.abs(real);
            }
            double q = real / imaginary;
            return Math.abs(imaginary) * Math.sqrt(1 + q * q);
        } else {
            if (real == 0.0) {
                return Math.abs(imaginary);
            }
            double q = imaginary / real;
            return Math.abs(real) * Math.sqrt(1 + q * q);
        }
    }

    /**
     * 矢径
     *
     * @return 矢径
     */
    public Double rho() {
        return this.abs();
    }

    /**
     * 主值幅角
     *
     * @return Double 主值幅角
     */
    public Double theta() {
        return this.getArgument();
    }

    /**
     * 复数的模
     *
     * @return 复数的模
     */
    public Double mode() {
        return this.abs();
    }

    /**
     * 主值幅角 (theta) 的别名
     *
     * @return Double
     */
    public Double angle() {
        return this.getArgument();
    }

    /**
     * 实部与虚部数据进行互换
     * @return 即做一个对角线上的镜像点
     */
    public Complex swap(){
        return new Complex(super.swap());
    }

    /**
     * 复数求反
     *
     * @return 复数求反
     */
    public Complex negate() {
        if (isNaN) return NaN;
        return new Complex(-this.real(), -this.imaginary());
    }

    /**
     * 共轭复数
     *
     * @return 共轭复数
     */
    public Complex conjugate() {
        if (isNaN) return NaN;
        return new Complex(this.real(), -this.imaginary());
    }

    /**
     * 把一个复数伸缩 a倍
     *
     * @param a 把一个复数伸缩 a倍
     * @return Complex
     */
    public Complex scale(final Number a) {
        return Complex.rhoTheta(this.rho() * a.doubleValue(), this.theta());
    }

    /**
     * 旋转角度
     *
     * @param alpha 旋转角度
     * @return Complex
     */
    public Complex rotate(final Number alpha) {
        final var rho = this.rho();
        return Complex.rhoTheta(rho, this.theta() + alpha.doubleValue());
    }

    /**
     * 对向量进行仿射变换
     *
     * @param scale 缩放尺寸
     * @param theta 旋转角度
     * @return 新的复数结构
     */
    public Complex affine(final Number scale, final Number theta) {
        if (isNaN) return NaN;
        return this.scale(scale).rotate(theta);
    }

    /**
     * 复数求和
     *
     * @param c 复数
     * @return Complex
     */
    public Complex add(final Complex c) {
        if (isNaN) return NaN;
        return xY(this.x() + c.x(), this.y() + c.y());
    }

    /**
     * 复数求和
     *
     * @param c 另一个加数
     * @return Complex
     */
    public Complex add(final Number c) {
        return this.add(Complex.C1(c));
    }

    /**
     * 复数求差
     *
     * @param c 复数
     * @return Complex
     */
    public Complex sub(final Complex c) {
        if (isNaN) return NaN;
        return xY(this.x() - c.x(), this.y() - c.y());
    }

    /**
     * 复数求差
     *
     * @param c 另一个减数
     * @return Complex
     */
    public Complex sub(final Number c) {
        return sub(Complex.C1(c));
    }

    /**
     * 复数求差 :c.sub(this);
     *
     * @param c 复数
     * @return Complex
     */
    public Complex sub2(final Complex c) {
        if (isNaN) return NaN;
        return c.sub(this);
    }

    /**
     * 复数求差 :c.sub(this);
     *
     * @param c 复数
     * @return Complex
     */
    public Complex sub2(final Number c) {
        if (isNaN) return NaN;
        return Complex.C1(c).sub(this);
    }

    /**
     * 复数求积
     *
     * @param c 复数
     * @return Complex
     */
    public Complex mul(final Complex c) {
        if (isNaN) return NaN;
        return xY(this.x() * c.x() - this.y() * c.y(), this.x() * c.y() + this.y() * c.x());
    }

    /**
     * Returns a {@code Complex} whose value is {@code this * factor}, with {@code factor}
     * interpreted as a integer number.
     *
     * @param factor value to be multiplied by this {@code Complex}.
     * @return {@code this * factor}.
     */
    public Complex mul(final Number factor) {
        if (isNaN) return NaN;

        final var real = this.real();
        final var imaginary = this.imaginary();

        if (Double.isInfinite(real) ||
                Double.isInfinite(imaginary)) {
            return INF;
        }
        return C(real * factor.doubleValue(), imaginary * factor.doubleValue());
    }

    /**
     * 复数求商
     *
     * @param c 复数
     * @return Complex
     */
    public Complex div(final Complex c) {
        if (isNaN) return NaN;
        final var r = c.x() * c.x() + c.y() * c.y();
        return xY((this.x() * c.x() + this.y() * c.y()) / r, (this.y() * c.x() - this.x() * c.y()) / r);
    }

    /**
     * Returns a {@code Complex} whose value is {@code (this / divisor)},
     * with {@code divisor} interpreted as a real number.
     *
     * @param divisor Value by which this {@code Complex} is to be divided.
     * @return {@code this / divisor}.
     */
    public Complex div(final Number divisor) {
        if (isNaN) return NaN;
        final var real = this.real();
        final var imaginary = this.imaginary();
        final var final_divisor = divisor.doubleValue();
        if (final_divisor == 0d) {
            return NaN;
        }
        if (Double.isInfinite(final_divisor)) {
            return ZERO;
        }
        return C(real / final_divisor, imaginary / final_divisor);
    }

    /**
     * 复数求差:c.div(this);
     *
     * @param c 复数
     * @return Complex
     */
    public Complex div2(final Complex c) {
        if (isNaN) return NaN;
        return c.div(this);
    }

    /**
     * 复数求差:c.div(this);
     *
     * @param c 复数
     * @return Complex
     */
    public Complex div2(final Number c) {
        if (isNaN) return NaN;
        return Complex.C(c).div(this);
    }

    /**
     * Returns of value of this complex number raised to the power of {@code x}.
     * Implements the formula:
     * <pre>
     *  <code>
     *   y<sup>x</sup> = exp(x&middot;log(y))
     *  </code>
     * </pre>
     * where {@code exp} and {@code log} are {@link #exp} and
     * {@link #log}, respectively.
     *
     * @param x exponent to which this {@code Complex} is to be raised.
     * @return <code> this<sup>x</sup></code>
     */
    public Complex pow(final Complex x) {
        if (isNaN) return NaN;
        return this.log().mul(x).exp();
    }

    /**
     * Returns of value of this complex number raised to the power of {@code x}.
     *
     * @param x exponent to which this {@code Complex} is to be raised.
     * @return <code>this<sup>x</sup></code>.
     */
    public Complex pow(final Number x) {
        if (isNaN) return NaN;
        return this.log().mul(x).exp();
    }

    /**
     * Returns of value of this complex number raised to the power of {@code x}.
     * Implements the formula:
     * 
     * <pre>
     *  <code>
     *   x<sup>this</sup> = exp(this&middot;log(x))
     *  </code>
     * </pre>
     * 
     * where {@code exp} and {@code log} are {@link #exp} and {@link #log},
     * respectively.
     *
     * @param x 底数
     * @return <code> x<sup>this</sup></code>
     */
    public Complex pow2(final Complex x) {
        return x.pow(this);
    }

    /**
     * Returns of value of this complex number raised to the power of {@code x}.
     * Implements the formula:
     * 
     * <pre>
     *  <code>
     *   x<sup>this</sup> = exp(this&middot;log(x))
     *  </code>
     * </pre>
     * 
     * where {@code exp} and {@code log} are {@link #exp} and {@link #log},
     * respectively.
     *
     * @param x 底数
     * @return <code> x<sup>this</sup></code>
     */
    public Complex pow2(final Number x) {
        return Complex.C(x).pow(this);
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
     * square root</a> of this complex number.
     * Implements the following algorithm to compute {@code sqrt(a + bi)}:
     * <ol><li>Let {@code t = sqrt((|a| + |a + bi|) / 2)}</li>
     * <li><pre>if {@code  a &#8805; 0} return {@code t + (b/2t)i}
     *  else return {@code |b|/2t + sign(b)t i }</pre></li>
     * </ol>
     * where <ul>
     * <li>{@code |a| = }{@link Math#abs}(a)</li>
     * <li>{@code |a + bi| = }{@link Complex#abs}(a + bi)</li>
     * <li>{@code sign(b) =  }{@link Math#copySign(double, double) copySign(1d, b)}
     * </ul>
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.
     * <pre>
     *  Examples:
     *  <code>
     *   sqrt(1 &plusmn; INFINITY i) = INFINITY + NaN i
     *   sqrt(INFINITY + i) = INFINITY + 0i
     *   sqrt(-INFINITY + i) = 0 + INFINITY i
     *   sqrt(INFINITY &plusmn; INFINITY i) = INFINITY + NaN i
     *   sqrt(-INFINITY &plusmn; INFINITY i) = NaN &plusmn; INFINITY i
     *  </code>
     * </pre>
     *
     * @return the square root of {@code this}.
     */
    public Complex sqrt() {

        if (isNaN) return NaN;

        final var imaginary = this.imaginary();
        final var real = this.real();
        if (real == 0.0 && imaginary == 0.0) {
            return C(0.0, 0.0);
        }

        double t = Math.sqrt((Math.abs(real) + abs()) / 2.0);
        if (real >= 0.0) {
            return C(t, imaginary / (2.0 * t));
        } else {
            return C(Math.abs(imaginary) / (2.0 * t),
                    Math.copySign(1d, imaginary) * t);
        }
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top">
     * square root</a> of <code>1 - this<sup>2</sup></code> for this complex
     * number.
     * Computes the result directly as
     * {@code sqrt(ONE.subtract(z.multiply(z)))}.
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.
     *
     * @return the square root of <code>1 - this<sup>2</sup></code>.
     */
    public Complex sqrt1z() {
        return C(1.0, 0.0).sub(this.mul(this)).sqrt();
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/Cosine.html" TARGET="_top">
     * cosine</a> of this complex number.
     * Implements the formula:
     * <p>
     * {@code cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i}
     * </p><p>
     * where the (real) functions on the right-hand side are
     * {@link Math#sin}, {@link Math#cos},
     * {@link Math#cosh} and {@link Math#sinh}.
     * </p><p>
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.</p>
     * <pre>
     *  Examples:
     *  <code>
     *   cos(1 &plusmn; INFINITY i) = 1 \u2213 INFINITY i
     *   cos(&plusmn;INFINITY + i) = NaN + NaN i
     *   cos(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *  </code>
     * </pre>
     *
     * @return the cosine of this complex number.
     */
    public Complex cos() {
        if (isNaN) return NaN;
        final var imaginary = this.imaginary();
        final var real = this.real();
        return C(Math.cos(real) * Math.cosh(imaginary),
                -Math.sin(real) * Math.sinh(imaginary));
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/HyperbolicCosine.html" TARGET="_top">
     * hyperbolic cosine</a> of this complex number.
     * Implements the formula:
     * <pre>
     *  <code>
     *   cosh(a + bi) = cosh(a)cos(b) + sinh(a)sin(b)i
     *  </code>
     * </pre>
     * where the (real) functions on the right-hand side are
     * {@link Math#sin}, {@link Math#cos},
     * {@link Math#cosh} and {@link Math#sinh}.
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.
     * <pre>
     *  Examples:
     *  <code>
     *   cosh(1 &plusmn; INFINITY i) = NaN + NaN i
     *   cosh(&plusmn;INFINITY + i) = INFINITY &plusmn; INFINITY i
     *   cosh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *  </code>
     * </pre>
     *
     * @return the hyperbolic cosine of this complex number.
     */
    public Complex cosh() {
        if (isNaN) return NaN;
        final var imaginary = this.imaginary();
        final var real = this.real();
        return C(Math.cosh(real) * Math.cos(imaginary),
                Math.sinh(real) * Math.sin(imaginary));
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/InverseCosine.html" TARGET="_top">
     * inverse cosine</a> of this complex number.
     * Implements the formula:
     * <p>
     * {@code acos(z) = -i (log(z + i (sqrt(1 - z<sup>2</sup>))))}
     * </p>
     *
     * @return the inverse cosine of this complex number.
     */
    public Complex acos() {
        if (isNaN) return NaN;
        return this.add(this.sqrt1z().mul(I)).log().mul(I.negate());
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/Sine.html" TARGET="_top">
     * sine</a>
     * of this complex number.
     * Implements the formula:
     * <pre>
     *  <code>
     *   sin(a + bi) = sin(a)cosh(b) - cos(a)sinh(b)i
     *  </code>
     * </pre>
     * where the (real) functions on the right-hand side are
     * {@link Math#sin}, {@link Math#cos},
     * {@link Math#cosh} and {@link Math#sinh}.
     * <p>
     * Returns {@link Complex#NaN} if either real or imaginary part of the
     * input argument is {@code NaN}.
     * </p><p>
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or {@code NaN} values returned in parts of the result.
     * <pre>
     *  Examples:
     *  <code>
     *   sin(1 &plusmn; INFINITY i) = 1 &plusmn; INFINITY i
     *   sin(&plusmn;INFINITY + i) = NaN + NaN i
     *   sin(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *  </code>
     * </pre>
     *
     * @return the sine of this complex number.
     */
    public Complex sin() {
        if (isNaN) {
            return NaN;
        }
        final var imaginary = this.imaginary();
        final var real = this.real();
        return C(Math.sin(real) * Math.cosh(imaginary),
                Math.cos(real) * Math.sinh(imaginary));
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/HyperbolicSine.html" TARGET="_top">
     * hyperbolic sine</a> of this complex number.
     * Implements the formula:
     * <pre>
     *  <code>
     *   sinh(a + bi) = sinh(a)cos(b)) + cosh(a)sin(b)i
     *  </code>
     * </pre>
     * where the (real) functions on the right-hand side are
     * {@link Math#sin}, {@link Math#cos},
     * {@link Math#cosh} and {@link Math#sinh}.
     * <p>
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.
     * <pre>
     *  Examples:
     *  <code>
     *   sinh(1 &plusmn; INFINITY i) = NaN + NaN i
     *   sinh(&plusmn;INFINITY + i) = &plusmn; INFINITY + INFINITY i
     *   sinh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *  </code>
     * </pre>
     *
     * @return the hyperbolic sine of {@code this}.
     */
    public Complex sinh() {
        if (isNaN) return NaN;
        final var imaginary = this.imaginary();
        final var real = this.real();
        return C(Math.sinh(real) * Math.cos(imaginary),
                Math.cosh(real) * Math.sin(imaginary));
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/InverseSine.html" TARGET="_top">
     * inverse sine</a> of this complex number.
     * Implements the formula:
     * <p>
     * {@code asin(z) = -i (log(sqrt(1 - z<sup>2</sup>) + iz))}
     * </p>
     *
     * @return the inverse sine of this complex number.
     */
    public Complex asin() {
        if (isNaN) return NaN;
        return sqrt1z().add(this.mul(I)).log().mul(I.negate());
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/InverseTangent.html" TARGET="_top">
     * inverse tangent</a> of this complex number.
     * Implements the formula:
     * <p>
     * {@code atan(z) = (i/2) log((i + z)/(i - z))}
     * </p>
     *
     * @return the inverse tangent of this complex number
     */
    Complex tan() {
        final var imaginary = this.imaginary();
        final var real = this.real();
        if (imaginary > 20.0) {
            return C(0.0, 1.0);
        }
        if (imaginary < -20.0) {
            return C(0.0, -1.0);
        }

        double real2 = 2.0 * real;
        double imaginary2 = 2.0 * imaginary;
        double d = Math.cos(real2) + Math.cosh(imaginary2);

        return C(Math.sin(real2) / d,
                Math.sinh(imaginary2) / d);
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/HyperbolicTangent.html" TARGET="_top">
     * hyperbolic tangent</a> of this complex number.
     * Implements the formula:
     * <pre>
     *  <code>
     *   tan(a + bi) = sinh(2a)/(cosh(2a)+cos(2b)) + [sin(2b)/(cosh(2a)+cos(2b))]i
     *  </code>
     * </pre>
     * where the (real) functions on the right-hand side are
     * {@link Math#sin}, {@link Math#cos}, {@link Math#cosh} and
     * {@link Math#sinh}.
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.
     * <pre>
     *  Examples:
     *  <code>
     *   tanh(a &plusmn; INFINITY i) = NaN + NaN i
     *   tanh(&plusmn;INFINITY + bi) = &plusmn;1 + 0 i
     *   tanh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *   tanh(0 + (&pi;/2)i) = NaN + INFINITY i
     *  </code>
     * </pre>
     *
     * @return the hyperbolic tangent of {@code this}.
     */
    public Complex tanh() {
        if (isNaN) return NaN;

        final var imaginary = this.imaginary();
        final var real = this.real();

        if (real > 20.0) {
            return C(1.0, 0.0);
        }
        if (real < -20.0) {
            return C(-1.0, 0.0);
        }
        double real2 = 2.0 * real;
        double imaginary2 = 2.0 * imaginary;
        double d = Math.cosh(real2) + Math.cos(imaginary2);

        return C(Math.sinh(real2) / d,
                Math.sin(imaginary2) / d);
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/InverseTangent.html" TARGET="_top">
     * inverse tangent</a> of this complex number.
     * Implements the formula:
     * <p>
     * {@code atan(z) = (i/2) log((i + z)/(i - z))}
     * </p>
     *
     * @return the inverse tangent of this complex number
     */
    public Complex atan() {
        if (isNaN) return NaN;
        return this.add(I).div(I.sub(this)).log()
                .mul(I.div(C(2.0, 0.0)));
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/ExponentialFunction.html" TARGET="_top">
     * exponential function</a> of this complex number.
     * Implements the formula:
     * <pre>
     *  <code>
     *   exp(a + bi) = exp(a)cos(b) + exp(a)sin(b)i
     *  </code>
     * </pre>
     * where the (real) functions on the right-hand side are
     * {@link Math#exp}, {@link Math#cos}, and
     * {@link Math#sin}.
     * Infinite values in real or imaginary parts of the input may result in
     * infinite or NaN values returned in parts of the result.
     * <pre>
     *  Examples:
     *  <code>
     *   exp(1 &plusmn; INFINITY i) = NaN + NaN i
     *   exp(INFINITY + i) = INFINITY + INFINITY i
     *   exp(-INFINITY + i) = 0 + 0i
     *   exp(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *  </code>
     * </pre>
     *
     * @return <code><i>e</i><sup>this</sup></code>.
     */
    public Complex exp() {
        if (isNaN) return NaN;
        final var imaginary = this.imaginary();
        final var real = this.real();
        double expReal = Math.exp(real);
        return C(expReal * Math.cos(imaginary),
                expReal * Math.sin(imaginary));
    }

    /**
     * Compute the
     * <a href="http://mathworld.wolfram.com/HyperbolicCosine.html" TARGET="_top">
     * hyperbolic cosine</a> of this complex number. Implements the formula:
     * 
     * <pre>
     *  <code>
     *   cosh(a + bi) = cosh(a)cos(b) + sinh(a)sin(b)i
     *  </code>
     * </pre>
     * 
     * where the (real) functions on the right-hand side are {@link Math#sin},
     * {@link Math#cos}, {@link Math#cosh} and {@link Math#sinh}. Infinite values in
     * real or imaginary parts of the input may result in infinite or NaN values
     * returned in parts of the result.
     * 
     * <pre>
     *  Examples:
     *  <code>
     *   cosh(1 &plusmn; INFINITY i) = NaN + NaN i
     *   cosh(&plusmn;INFINITY + i) = INFINITY &plusmn; INFINITY i
     *   cosh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
     *  </code>
     * </pre>
     *
     * @return the hyperbolic cosine of this complex number.
     */
    public Complex log() {
        if (isNaN)
            return NaN;
        final var imaginary = this.imaginary();
        final var real = this.real();
        return C(Math.log(abs()), Math.atan2(imaginary, real));
    }

    /**
     * Returns the multiplicative inverse of this element.
     *
     * @reurn Returns:the inverse of this.
     */
    public Complex reciprocal() {
        if (isNaN) {
            return NaN;
        }

        final var imaginary = this.imaginary();
        final var real = this.real();

        if (real == 0.0 && imaginary == 0.0) {
            return INF;
        }

        if (isInfinite) {
            return ZERO;
        }

        if (Math.abs(real) < Math.abs(imaginary)) {
            double q = real / imaginary;
            double scale = 1. / (real * q + imaginary);
            return C(scale * q, -scale);
        } else {
            double q = imaginary / real;
            double scale = 1. / (imaginary * q + real);
            return C(scale, -scale * q);
        }
    }

    /**
     * Computes the n-th roots of this complex number.
     * The nth roots are defined by the formula:
     * <pre>
     *  <code>
     *   z<sub>k</sub> = abs<sup>1/n</sup> (cos(phi + 2&pi;k/n) + i (sin(phi + 2&pi;k/n))
     *  </code>
     * </pre>
     * for <i>{@code k=0, 1, ..., n-1}</i>, where {@code abs} and {@code phi}
     * are respectively the {@link #abs() modulus} and
     * {@link #getArgument() argument} of this complex number.
     *
     * @param n Degree of root.
     * @return a List of all {@code n}-th roots of {@code this}.
     */
    public List<Complex> nthRoot(final int n) {
        final List<Complex> result = new ArrayList<>();
        if (isNaN) {
            result.add(NaN);
            return result;
        }
    
        if (isInfinite()) {
            result.add(INF);
            return result;
        }
    
        if (n <= 0) {
            try {
                throw new Exception("参数n需要是大于0的整数:" + n);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
        // nth root of abs -- faster / more accurate to use a solver here?
        final double nthRootOfAbs = Math.pow(abs(), 1.0 / n);
    
        // Compute nth roots of complex number with k = 0, 1, ... n-1
        final double nthPhi = getArgument() / n;
        final double slice = 2 * Math.PI / n;
        double innerPart = nthPhi;
        for (int k = 0; k < n; k++) {
            // inner part
            final double realPart = nthRootOfAbs * Math.cos(innerPart);
            final double imaginaryPart = nthRootOfAbs * Math.sin(innerPart);
            result.add(new Complex(realPart, imaginaryPart));
            innerPart += slice;
        }
    
        return result;
    }

    /**
     * 把一个复数转换成一个1*1的矩阵
     *
     * @return 1*1的矩阵
     */
    public ComplexMatrix oneXone() {
        return ComplexMatrix.of(this);
    }

    /**
     * 把一个复数复制 n次组成一个向量
     *
     * @param n 重复次数 大于0的正整数
     * @return 一维向量
     */
    public ComplexMatrix repeatV(final Number n) {
        return ComplexMatrix.V(XVec.OF(Math.abs(n.intValue()), i -> this));
    }

    /**
     * 把一个复数 与另外一组复数拼接成向量，当前复数为第一元素
     *
     * @param cc 另外一组复数
     * @return 一维向量
     */
    public ComplexMatrix appendV(final Complex... cc) {
        return ComplexMatrix.V(XVec.OF(this).concat(XVec.OF(cc)));
    }

    /**
     * 把一个复数 与另外一组复数拼接成向量，当前复数为末尾元素
     *
     * @param cc 另外一组复数
     * @return 一维向量
     */
    public ComplexMatrix prependV(final Complex... cc) {
        return ComplexMatrix.V(XVec.OF(cc).concat(XVec.OF(this)));
    }

    /**
     * 对当前复数 应用函数 x2u,即 x2u.apply(this)
     *
     * @param x2u 变换函数 x->u
     * @param <U> 结果类型
     * @return 变换结果
     */
    public <U> U fx(final Function<Complex, U> x2u) {
        return x2u.apply(this);
    }

    /**
     *  F fx 的缩写 第一个F 表示 Functor, fx 表示 变换函数 <br>
     * 对当前复数 应用函数 x2real,即 C(x2real.apply(this))
     *
     * @param x2real 变换函数 x->num
     * @return 变换结果
     */
    public Complex ffx(final Function<Complex, Number> x2real) {
        return C(x2real.apply(this));
    }

    /**
     * real fx <br>
     * 对当前复数 应用函数 x2num,即 x2num.apply(this.real)
     *
     * @param x2num 变换函数 x->num
     * @return 变换结果
     */
    public Complex rfx(final Function<Double, Number> x2num) {
        return C(x2num.apply(this.real()));
    }

    /**
     * imaginary fx <br>
     * 对当前复数 应用函数 x2num,即 x2num.apply(thi.imaginary)
     *
     * @param x2num 变换函数 x->num
     * @return 变换结果
     */
    public Complex ifx(final Function<Double, Number> x2num) {
        return C(x2num.apply(this.imaginary()));
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param c 复数形式的 移动偏移位置（实部水平,虚部垂直)
     * @return 移动后的结果位置（向量）
     */
    public Complex offset(final Complex c) {
        return this.add(c);
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex offset(final Number x, final Number y) {
        return this.add(C(x, y));
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex right(final Number x) {
        return this.offset(x, 0);
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex left(final Number x) {
        return this.offset(-x.doubleValue(), 0);
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex top(final Number y) {
        return this.offset(0, y.intValue());
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex bottom(final Number y) {
        return this.offset(0, -y.doubleValue());
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex leftTop(final Number x, final Number y) {
        return this.offset(-x.doubleValue(), y);
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex rightTop(final Number x, final Number y) {
        return this.offset(x, y);
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex leftBottom(final Number x, final Number y) {
        return this.offset(-x.doubleValue(), -y.doubleValue());
    }

    /**
     * 视当前复数为一个平面点，水平为实轴，垂直为虚轴，然后在平面上进行 水平 与 垂直 方向 移动
     *
     * @param x 水平移动距离 实轴移动
     * @param y 垂直移动距离 虚轴移动
     * @return 移动后的结果位置（向量）
     */
    public Complex rightBottom(final Number x, final Number y) {
        return this.offset(x, -y.doubleValue());
    }

    /**
     * 计算当前 点 相对于 c 的相对关系
     *
     * @param c 标准位置
     * @return 计算当前 点 相对于 c 的相对位置
     */
    public Relation relative(final Complex c) {

        if (this.equals(c)) return Relation.EQ;
        else { // 不等的位置判断
            final var real_delta = eps_compare(this.real(), c.real()); // 实部差
            final var imaginary_delta = eps_compare(this.imaginary(), c.imaginary()); // 虚部差

            if (real_delta == 0 && imaginary_delta > 0) return Relation.TP;
            else if (real_delta == 0 && imaginary_delta > 0) return Relation.BT;

            else if (real_delta < 0 && imaginary_delta == 0) return Relation.LF;
            else if (real_delta > 0 && imaginary_delta == 0) return Relation.RI;

            else if (real_delta < 0 && imaginary_delta > 0) return Relation.LT;
            else if (real_delta < 0 && imaginary_delta < 0) return Relation.LB;

            else if (real_delta > 0 && imaginary_delta > 0) return Relation.RT;
            else if (real_delta > 0 && imaginary_delta < 0) return Relation.RB;
            else return Relation.NE;
        } // if
    }

    /**
     * 把实部与虚部拼接成数组给予犯规
     *
     * @return [real, imaginary]
     */
    public Double[] dblA() {
        return new Double[]{this.real(), this.imaginary()};
    }

    /**
     * 把实部与虚部拼接成NVec向量
     *
     * @return [real, imaginary]
     */
    public NVec toNVec() {
        return NVec.of(this.dblA());
    }

    /**
     * Checks whether either or both parts of this complex number is
     * {@code NaN}.
     *
     * @return true if either or both parts of this complex number is
     * {@code NaN}; false otherwise.
     */
    public boolean isNaN() {
        return isNaN;
    }

    /**
     * Checks whether either the real or imaginary part of this complex number
     * takes an infinite value (either {@code Double.POSITIVE_INFINITY} or
     * {@code Double.NEGATIVE_INFINITY}) and neither part
     * is {@code NaN}.
     *
     * @return true if one or both parts of this complex number are infinite
     * and neither part is {@code NaN}.
     */
    public boolean isInfinite() {
        return isInfinite;
    }

    /**
     * Get a hashCode for the complex number.
     * Any {@code Double.NaN} value in real or imaginary part produces
     * the same hash code {@code 7}.
     *
     * @return a hash code value for this object.
     */
    @Override
    public int hashCode() {
        if (isNaN) {
            return 7;
        }
        final var imaginary = this.imaginary();
        final var real = this.real();
        return 37 * (17 * imaginary.hashCode() + real.hashCode());
    }

    /**
     * 判断两个复数是否相等 <br>
     * 判断标准是 实部的距离差 与 虚部距离差 是否都小于 EPS
     * 
     * @param c 复数
     * @return 两个复数是否相等
     */
    public boolean equals(final Complex c) {
        if (this.isInfinite || this.isNaN || c.isInfinite || c.isNaN)
            return false;
        final var r = Math.abs(this.real() - c.real()); // 实部的距离差
        final var i = Math.abs(this.imaginary() - c.imaginary()); // 虚部距离差
        return r < EPS && i < EPS;
    }

    /**
     * 格式化
     */
    public String toString() {
        return this.toString(6);
    }

    /**
     * 数据格式化
     *
     * @param n 数据精度
     * @return 数据格式化
     */
    public String toString(final int n) {
        final var nf = (DecimalFormat) DecimalFormat.getInstance();
        nf.setMaximumFractionDigits(n);
        final var eps = 1e-5;
        if (Math.abs(this.real()) < eps) {
            return this.imaginary() < eps ? "0" : String.format("%si", nf.format(this.imaginary()));
        } else if (Math.abs(this.imaginary()) <= eps) {
            return String.format("%s", nf.format(this.real()));
        } else {
            return String.format("%s+%si", nf.format(this.real()), nf.format(this.imaginary()));
        }
    }

    /**
     * 傅里叶矩阵
     *
     * @param size 傅里叶 的维度
     * @return 傅里叶矩阵
     */
    public static ComplexMatrix fourierX(final int size) {
        return fourierX(size, false);
    }

    /**
     * 傅里叶矩阵
     *
     * @param size    傅里叶 的维度
     * @param reverse 是否对矩阵取反
     * @return 傅里叶矩阵
     */
    public static ComplexMatrix fourierX(final int size, final boolean reverse) {
        final var xxvec = XVec.OF(Complex.fourierMx(size, reverse)).fmap((Function<Complex[], XVec<Complex>>) XVec::OF);
        return new ComplexMatrix(xxvec);
    }

    /**
     * 傅里叶矩阵
     *
     * @param size 傅里叶 的维度
     * @return 傅里叶矩阵
     */
    public static Complex[][] fourierMx(final int size) {
        return Complex.fourierMx(size, false);
    }

    /**
     * 傅里叶矩阵
     *
     * @param size    傅里叶 的维度
     * @param reverse 是否对矩阵取反
     * @return 傅里叶矩阵
     */
    public static Complex[][] fourierMx(final int size, boolean reverse) {
        final var r = 1 / Math.sqrt(size); // 主题
        final var w = (reverse ? -1 : 1) * 2 * PI / size;
        final var omegas = NVec.of(size, i -> i * w);
        return NVec.of(size, i -> i).flatMap(i -> omegas.map(omega_n -> Complex.rhoTheta(r, i * omega_n)))
                .collect(XVec.xveclc()).reshape(Complex[][].class, size, size);
    }

    /**
     * 构造复数对象
     *
     * @param a 实数部分
     * @param b 虚数部分
     * @return 复数对象
     */
    public static Complex C(final Number a, final Number b) {
        return new Complex(a, b);
    }

    /**
     * 构造复数对象 C1的别名函数
     *
     * @param a 实数部分
     * @return 复数对象
     */
    public static Complex C(final Number a) {
        return C1(a);
    }

    /**
     * 构造复数对象
     *
     * @param a 实数部分
     * @return 复数对象
     */
    public static Complex C1(final Number a) {
        return new Complex(a, 0);
    }

    /**
     * 构造复数对象
     *
     * @param b 虚数部分
     * @return 复数对象
     */
    public static Complex C2(final Number b) {
        return new Complex(0, b);
    }

    /**
     * 构造复数对象 :Complex(a, b).exp() 的简写
     *
     * @param b 虚数部分
     * @return 复数对象
     */
    public static Complex EXP(final Number a, final Number b) {
        return new Complex(a, b).exp();
    }

    /**
     * 构造复数对象 :Complex(0, b).exp(); 的简写
     *
     * @param b 虚数部分
     * @return 复数对象
     */
    public static Complex EXP(final Number b) {
        return new Complex(0, b).exp();
    }

    /**
     * 生成数据向量
     *
     * @param nums 数据向量
     * @return 数据向量
     */
    public static XVec<Complex> ofX(final Number... nums) {
        return XVec.OF(nums).fmap(x -> Complex.xY(x, 0));
    }

    /**
     * 生成数据向量
     *
     * @param nums 数据向量
     * @return 数据向量
     */
    public static XVec<Complex> ofX(final Complex... nums) {
        return XVec.OF(nums);
    }

    /**
     * 构造复数
     *
     * @param a 实部
     * @param b 虚部
     * @return Complex
     */
    public static Complex xY(final Number a, final Number b) {
        return new Complex(a.doubleValue(), b.doubleValue());
    }

    /**
     * 构造复数
     *
     * @param rho   复数的模
     * @param theta 复数的幅角
     * @return Complex
     */
    public static Complex rhoTheta(final Number rho, final Number theta) {
        return new Complex(rho.doubleValue() * Math.cos(theta.doubleValue()),
                rho.doubleValue() * Math.sin(theta.doubleValue()));
    }

    /**
     * 构造复数
     *
     * @param theta 复数的幅角
     * @return Complex
     */
    public static Complex rhoTheta(final Number theta) {
        return Complex.rhoTheta(1, theta);
    }

    /**
     * 拉链归并器
     *
     * @param list 第二位置向量,当长度不足之后,会循环补填以实现数据对应。
     * @return 把T类型的流归结成PVec&lt;K,V&gt; 类型的归集器
     */
    public static Collector<Double, ?, XVec<Complex>> zipclc(final List<? extends Number> list) {
        final var vv = list.stream().collect(Range.aaclc(e -> e));
        final var n = vv.length;
        final var ai = new AtomicInteger();
        return Collector.of(ArrayList::new,
                (List<Complex> aa, Double k) -> aa.add(Complex.xY(k, vv[ai.getAndIncrement() % n])), (aa, bb) -> {
                    aa.addAll(bb);
                    return aa;
                }, XVec::OF);
    }

    /**
     * 实部虚部构建器归集器
     *
     * @param mapper 数据映射器
     * @return 复数归集器
     */
    public static Collector<Number, ?, XVec<Complex>> xyclc(final Function<Double, ? extends Number> mapper) {
        return Collector.of(ArrayList::new,
                (List<Complex> aa, Number k) -> aa.add(Complex.xY(k, mapper.apply(k.doubleValue()))), (aa, bb) -> {
                    aa.addAll(bb);
                    return aa;
                }, XVec::OF);
    }

    /**
     * 虚部实部归集器
     *
     * @param mapper 数据映射器
     * @return 复数归集器
     */
    public static Collector<Number, ?, XVec<Complex>> yxclc(final Function<Double, ? extends Number> mapper) {
        return Collector.of(ArrayList::new,
                (List<Complex> aa, Number k) -> aa.add(Complex.xY(mapper.apply(k.doubleValue()), k)), (aa, bb) -> {
                    aa.addAll(bb);
                    return aa;
                }, XVec::OF);
    }

    /**
     * 带有误差的数值比较 eps compare
     * @param a 一个元素
     * @param b 另一个元素
     * @return 比较结果 a.compare(b) 的比较结果，当 a 与 b 的差小于 eps 的时候 视为相等
     */
    public static int  eps_compare(final Number a, final Number b) {
        return eps_compare(a,b,EPS);
    }

    /**
     * 带有误差的数值比较 eps compare
     * @param a 一个元素
     * @param b 另一个元素
     * @param eps 误差的忽略边界，大于零的浮点数
     * @return 比较结果 a.compare(b) 的比较结果，当 a 与 b 的差小于 eps 的时候 视为相等
     */
    public static int  eps_compare(final Number a, final Number b,final double eps) {
        final var d = a.doubleValue() - b.doubleValue(); // 偏差delta
        return Math.abs(d) < eps
                ? 0
                : d > 0 ? 1 : -1;
    }

    /**
     * Record whether this complex number is equal to NaN.
     */
    private final transient boolean isNaN;
    /**
     * Record whether this complex number is infinite.
     */
    private final transient boolean isInfinite;

    /**
     * 构造一个0向量（二维）
     */
    public static Complex O = C1(0);
    /**
     * The square root of -1. A number representing "0.0 + 1.0i"
     */
    public static final Complex I = C(0.0, 1.0);
    // CHECKSTYLE: stop ConstantName
    /**
     * A complex number representing "NaN + NaNi"
     */
    public static final Complex NaN = C(Double.NaN, Double.NaN);
    // CHECKSTYLE: resume ConstantName
    /**
     * A complex number representing "+INF + INFi"
     */
    public static final Complex INF = C(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
    /**
     * A complex number representing "1.0 + 0.0i"
     */
    public static final Complex ONE = C(1.0, 0.0);
    /**
     * realOne <br>
     * A complex number representing "1 + 0.0i"
     */
    public static final Complex REALONE = C(1.0, 0.0);
    /**
     * realOne 负数 <br>
     * A complex number representing "1 + 0.0i"
     */
    public static final Complex NEG_REALONE = C(1.0, 0.0).mul(-1);
    /**
     * imginaryOne <br>
     * A complex number representing "0.0 + 1.0i"
     */
    public static final Complex IMGONE = C(0.0, 0.0);
    /**
     * imginaryOne  负数 <br>
     * A complex number representing "0.0 + 1.0i"
     */
    public static final Complex NEG_IMGONE = IMGONE.mul(-1);
    /**
     * 单位向量
     * A complex number representing "1.0 + 1.0i"
     */
    public static final Complex UNIT = C(1.0, 0.0);
    /**
     * 单位向量 负数
     * A complex number representing "1.0 + 1.0i"
     */
    public static final Complex NEG_UNIT = UNIT.mul(-1);
    /**
     * A complex number representing "0.0 + 0.0i"
     */
    public static final Complex ZERO = C(0.0, 0.0);
    /**
     * 无穷小量，用于做复数相等的时候的误差分析
     */
    public static  Double EPS = 1e-10;

    /**
     * 空间位置关系
     */
    public enum Relation {
        TP, // 上
        BT, // 下
        LF, // 左边
        RI, // 右边
        LT, // 左上
        RT, // 右上
        LB, // 左下
        RB, // 右下
        EQ, // 相等
        NE // 不存在 NOT EXIST
    }

}
